package com.yahoo.platform.util;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;

import org.apache.log4j.Logger;

public class DirectoryWatcher extends Observable {

	private Logger logger = Logger.getLogger(DirectoryWatcher.class);
	private WatchService watchService;
	private final Map<WatchKey, Path> directories = new HashMap<WatchKey, Path>();
	// private Executor executor = Executors.newSingleThreadExecutor();
	private static Thread thread = null;
	private boolean end = false;

	public DirectoryWatcher() {
	}

	public DirectoryWatcher(String dir) throws IOException {
		watchService = FileSystems.getDefault().newWatchService();
	}

	/**
	 * 开始监控目录（启动一个新的线程）
	 * 
	 * @param dir
	 *            绝对路径
	 */
	public void startWatch(final String dir) {
		Path start = Paths.get(dir);
		try {
			watchService = FileSystems.getDefault().newWatchService();
			registerTree(start);
		} catch (IOException ex) {
			logger.error("监控目录失败：" + start.toAbsolutePath());
			// logger.error("Failed to monitor directory：" +
			// start.toAbsolutePath(), ex, Locale.ENGLISH);
			return;
		}
		// LOG.info("开始监控目录：" + start.toAbsolutePath());
		// LOG.info("Start to monitor directory：" + start.toAbsolutePath(),
		// Locale.ENGLISH);
		thread = new Thread(new Runnable() {

			public void run() {
				watch();
			}

		});
		thread.start();
		// System.out.println("开始监控目录");
		logger.info("开始监控目录：" + start.toAbsolutePath());
		// TODO
		try {
			thread.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * 停止目录监控
	 */
	public void stopWatch() {
		if (thread != null) {
			thread.interrupt();
		}
	}

	private void watch() {
		try {
			while (!end) {
				final WatchKey key = watchService.take();
				if (key == null) {
					continue;
				}
				for (WatchEvent<?> watchEvent : key.pollEvents()) {
					final WatchEvent.Kind<?> kind = watchEvent.kind();
					// 忽略无效事件
					if (kind == StandardWatchEventKinds.OVERFLOW) {
						continue;
					}
					@SuppressWarnings("unchecked")
					final WatchEvent<Path> watchEventPath = (WatchEvent<Path>) watchEvent;
					// path是相对路径（相对于监控目录）
					final Path contextPath = watchEventPath.context();
					// LOG.debug("contextPath:"+contextPath);
					// 获取监控目录
					final Path directoryPath = directories.get(key);
					// LOG.debug("directoryPath:"+directoryPath);
					// 得到绝对路径
					final Path absolutePath = directoryPath
							.resolve(contextPath);
					// LOG.debug("absolutePath:"+absolutePath);
					// LOG.debug("kind:"+kind);

					// 判断事件类别
					switch (kind.name()) {
					case "ENTRY_CREATE":
						if (Files.isDirectory(absolutePath,
								LinkOption.NOFOLLOW_LINKS)) {
							// LOG.info("Create directory：" + absolutePath,
							// Locale.ENGLISH);
							// 为新增的目录及其所有子目录注册监控事件
							logger.info("新增目录：" + absolutePath);
							registerTree(absolutePath);
						} else {
							logger.info("新增文件：" + absolutePath);
							// LOG.info("Create file：" + absolutePath,
							// Locale.ENGLISH);
						}
						break;
					case "ENTRY_DELETE":
						if (Files.isDirectory(absolutePath,
								LinkOption.NOFOLLOW_LINKS)) {
							logger.info("删除目录：" + absolutePath);
						}else{
							logger.info("删除文件：" + absolutePath);
						}
						// LOG.info("Delete：" + absolutePath, Locale.ENGLISH);
						break;
					case "ENTRY_MODIFY":
						if (!Files.isDirectory(absolutePath,
								LinkOption.NOFOLLOW_LINKS)) {
							logger.info("修改文件：" + absolutePath);
						}
						break;
					}
					nofifiy(absolutePath, kind);
				}
				boolean valid = key.reset();
				if (!valid) {
					logger.info("停止监控目录：" + directories.get(key));
					directories.remove(key);
					if (directories.isEmpty()) {
						logger.error("退出监控");
						break;
					}
				}
			}
		} catch (IOException ex) {
			// LOG.info("监控目录线程退出");
		} catch (InterruptedException ex) {

		} finally {
			try {
				watchService.close();
				logger.info("关闭监控目录服务");
			} catch (IOException ex) {
				logger.error("关闭监控目录服务出错", ex);
			}
		}
	}

	private void nofifiy(Path absolutePath, Kind<?> kind) {
		// TODO Auto-generated method stub
		setChanged();
		notifyObservers(new FileSystemEventArgs(absolutePath, kind));
	}

	/**
	 * 为指定目录及其所有子目录注册监控事件
	 * 
	 * @param start
	 *            目录
	 * @throws IOException
	 */
	private void registerTree(Path start) throws IOException {
		Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
			@Override
			public FileVisitResult preVisitDirectory(Path dir,
					BasicFileAttributes attrs) throws IOException {
				registerPath(dir);
				return FileVisitResult.CONTINUE;
			}
		});
	}

	/**
	 * 为指定目录注册监控事件
	 * 
	 * @param path
	 * @throws IOException
	 */
	private void registerPath(Path path) throws IOException {
		// LOG.debug("监控目录:" + path);
		WatchKey key = path.register(watchService,
				StandardWatchEventKinds.ENTRY_CREATE,
				StandardWatchEventKinds.ENTRY_MODIFY,
				StandardWatchEventKinds.ENTRY_DELETE);
		directories.put(key, path);
	}

	public boolean isEnd() {
		return end;
	}

	public void setEnd(boolean end) {
		this.end = end;
	}
}
